#define lib_portaudio_c
#define LUA_LIB

extern "C" {
#include "../lua.h"
#include "../lauxlib.h"
#include "../lualib.h"

#include "../lj_def.h" // Included always for the typedefs
#include "../lj_lib.h" // Included always for the typedefs
}

#undef cast
#define GVL_EXCLUDE_STDINT 1 // lj2 has type typedefs

#include "portaudio/mixer.hpp"
#include "util.hpp"
#include <gvl/io/encoding.hpp>
#include <gvl/io/fstream.hpp>

/* ------------------------------------------------------------------------ */

LUA_CHECK_FUNC(Sound, sound, UDTYPE_PORTAUDIO_SOUND)

extern "C" {

#define LJLIB_MODULE_portaudio_sound_method

LJLIB_CF(portaudio_sound_method_play)
{
	Sound& sound = check_sound(L, 1);
	setnumV(L->top++, (lua_Number)(int32_t)mixer.add(&sound));
	return 1;
}

LJLIB_CF(portaudio_sound_method___gc)
{
	if(Sound* sound = static_cast<Sound*>(try_sound(L, 1)))
	{
		// TODO: Clearing the image tag can conceivably be missed by
		// traced calls. We need some other way to signal this.
		lua_clear_object_tag(L, 1);
		sound->~Sound();
	}

	return 0;
}

LJLIB_PUSH(top-1) LJLIB_SET(__index)

/* ------------------------------------------------------------------------ */

#define LJLIB_MODULE_portaudio

// TODO: __gc

LJLIB_PUSH(top-2) LJLIB_SET(!) // Sound metatable

LJLIB_CF(portaudio_load)
{
	char const* name = strdata(lj_lib_checkstr(L, 1));

	void* mem = lua_create_object(L, UDTYPE_PORTAUDIO_SOUND, sizeof(Sound));

	gvl::octet_stream_reader reader(gvl::stream_ptr(new gvl::fstream(name, "rb")));
	Sound* self = new (mem) Sound(reader);

	return 1;
}

LJLIB_CF(portaudio_set_speed)
{
	uint32_t id = (uint32_t)lj_lib_checkint(L, 1);
	double speed = lj_lib_checknum(L, 2);
	mixer.setSpeed(id, speed);
	return 0;
}

LJLIB_CF(portaudio_set_volume)
{
	uint32_t id = (uint32_t)lj_lib_checkint(L, 1);
	double volume = lj_lib_checknum(L, 2);
	uint32_t volumeInt = uint32_t(volume * 0x1000);
	mixer.setVolumes(id, volumeInt | (volumeInt << 16));
	return 0;
}

LJLIB_CF(portaudio_set_flags)
{
	uint32_t id = (uint32_t)lj_lib_checkint(L, 1);
	uint32_t flags = (uint32_t)lj_lib_checknum(L, 2);
	mixer.setFlags(id, flags);
	return 0;
}

#include "../lj_libdef.h"
#include "../lj_lib.h"

LUALIB_API int luaopen_portaudio(lua_State *L)
{
	mixer.start();
	
	LJ_LIB_REG(L, portaudio_sound_method);
	LJ_LIB_REG(L, portaudio);
	return 1;
}

} // extern "C"
